﻿using Infrastructure.Log;
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Infrastructure.Extensions;
using Infrastructure.Sockets.models;

namespace Infrastructure.Sockets
{
    /// <summary>
    /// 异步tcp客户端
    /// </summary>
    public class TcpClientAsync
    {
        /// <summary>
        /// 日志
        /// </summary>
        private readonly Logger _logger = new Logger(typeof(TcpClientAsync));

        /// <summary>
        /// 客户端连接
        /// </summary>
        private TcpClient _client;

        private int _tryConnectTimes = 0;

        /// <summary>
        /// 最大重连次数，0为不限次
        /// </summary>
        public int MaximumConnectTimes { get; set; } = 0;


        /// <summary>
        /// 通信使用的编码
        /// </summary>
        public Encoding Encoding { get; set; }

        /// <summary>
        /// 服务器地址
        /// </summary>
        public IPAddress ServerAddress { get; }
        /// <summary>
        /// 服务器端口
        /// </summary>
        public int ServerPort { get; }

        /// <summary>
        /// 错误信息
        /// </summary>
        public string Message { get; private set; }

        /// <summary>
        /// 是否在线
        /// </summary>
        public bool IsOnline => _client != null && _client.IsOnline();

        /// <summary>
        /// 是否运行
        /// </summary>
        public bool IsRun { get; private set; }

        /// <summary>
        /// 是否连接
        /// </summary>
        public bool IsConnected { get; private set; }
        private bool _hasTryReConnect;

        /// <summary>
        /// 异步客户端
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        public TcpClientAsync(string ip, int port) : this(IPAddress.Parse(ip), port)
        {
        }

        /// <summary>
        /// 异步客户端
        /// </summary>
        /// <param name="remoteIpAddress"></param>
        /// <param name="port"></param>
        public TcpClientAsync(IPAddress remoteIpAddress, int port)
        {
            Encoding = Encoding.ASCII;
            ServerAddress = remoteIpAddress;
            ServerPort = port;
        }

        /// <summary>
        /// 连接服务器
        /// </summary>
        /// <returns></returns>
        public bool Connect()
        {
            if (IsRun)
            {
                Message = "已启动";
                return false;
            }

            _client = new TcpClient();
            IsConnected = false;
            IsRun = true;
            try
            {
                _hasTryReConnect = true;
                _client.BeginConnect(ServerAddress, ServerPort, ConnectCallback, _client);
            }
            catch (Exception ex)
            {
                Message = ex.Message;
                _logger.Error(ex.Message, ex);
                IsRun = false;
            }

            return IsRun;
        }

        /// <summary>
        /// 重连
        /// </summary>
        public void ReConnect()
        {
            if (_hasTryReConnect)
            {
                return;
            }

            if (!IsRun)
            {
                Connect();
                return;
            }

            if (IsConnected && _client.IsOnline())
            {
                return;
            }



            _hasTryReConnect = true;

            _client.Close();

            _client = new TcpClient();
            try
            {
                IsConnected = false;
                _client.BeginConnect(ServerAddress, ServerPort, ConnectCallback, _client);
            }
            catch (Exception ex)
            {
                Message = ex.Message;
                _logger.Error(ex.Message, ex);
            }

        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        public void DisConnect()
        {
            _hasTryReConnect = false;
            IsConnected = false;
            IsRun = false;

            _client?.Close();
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            if (!_hasTryReConnect)
            {
                return;
            }
            _tryConnectTimes++;

            if (_tryConnectTimes <= MaximumConnectTimes && MaximumConnectTimes > 0)
            {
                return;
            }

            TcpClient t = (TcpClient)ar.AsyncState;

            try
            {
                t.EndConnect(ar);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                _logger.Warn(e.Message);
                OnServerConnectFailed();
            }
            if (t.Connected)
            {
                _tryConnectTimes = 0;
                _hasTryReConnect = false;
                IsConnected = true;
                OnServerConnected();
                Receive(t);
            }
            else
            {
                Thread.Sleep(1000);
                t.BeginConnect(ServerAddress, ServerPort, ConnectCallback, t);
            }
        }

        /// <summary>
        /// 发送
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool Send(byte[] value)
        {
            if (value == null || value.Length == 0)
            {
                throw new ArgumentNullException(nameof(value));
            }

            if (!_client.IsOnline())
            {
                IsConnected = false;
                OnServerDisConnected();
                return false;
            }

            try
            {
                _client.GetStream().Write(value, 0, value.Length);
            }
            catch (IOException ie)
            {
                IsConnected = false;
                _logger.Warn(ie.Message);
                OnServerDisConnected();
                return false;
            }
            catch (ObjectDisposedException oe)
            {
                IsConnected = false;
                _logger.Warn(oe.Message);
                OnServerDisConnected();
                return false;
            }
            catch (SocketException se)
            {
                IsConnected = false;
                _logger.Warn(se.Message);
                OnServerDisConnected();
                return false;
            }
            catch (Exception e)
            {
                Message = e.Message;
                _logger.Error(e.Message, e);
                //Console.WriteLine(e);
                OnServerDisConnected();
                throw;
            }

            return true;
        }

        /// <summary>
        /// 发送
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool Send(string value)
        {
            var data = Encoding.GetBytes(value);
            return Send(data);
        }

        private void Receive(TcpClient t)
        {
            try
            {

                TcpClientState state = new TcpClientState(t, new byte[1024 * 10]);

                t.Client.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ReceiveCallback, state);
            }
            catch (Exception e)
            {
                _logger.Error(e.Message, e);
                Console.WriteLine(e.ToString());
            }

        }

        private void ReceiveCallback(IAsyncResult ar)
        {

            try
            {
                TcpClientState state = (TcpClientState)ar.AsyncState;

                int bytesRead = state.TcpClient.Client.EndReceive(ar);
                if (bytesRead > 0)
                {
                    //state.Data = new byte[bytesRead];
                    //Array.Copy(state.Buffer, 0, state.Data, 0, bytesRead);
                    state.BufferLength = bytesRead;
                    RaiseDataReceived(state);
                    state.TcpClient.Client.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReceiveCallback, state);
                }
                else
                {
                    //TODO:服务器端关闭连接状态处理
                    DisConnect();
                    OnServerDisConnected();
                    Console.WriteLine($"End response");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine($"ReceiveCallback:\r\n" + e);
                _logger.Error(e.Message, e);
                //RaiseSocketException(null);
            }
        }


        public event EventHandler ServerConnectFailed;

        private void OnServerConnectFailed()
        {
            ServerConnected?.Invoke(this, EventArgs.Empty);
        }

        /// <summary>
        /// 与客户端的连接已建立事件
        /// </summary>
        public event EventHandler ServerConnected;

        private void OnServerConnected()
        {
            ServerConnected?.Invoke(this, EventArgs.Empty);
        }

        /// <summary>
        /// 与客户端断开连接
        /// </summary>
        public event EventHandler ServerDisConnected;

        private void OnServerDisConnected()
        {
            ServerDisConnected?.Invoke(this, EventArgs.Empty);
        }

        /// <summary>
        /// 接收到数据事件
        /// </summary>
        public event EventHandler<TcpClientEventArgs> DataReceived;

        private void RaiseDataReceived(TcpClientState state)
        {
            DataReceived?.Invoke(this, new TcpClientEventArgs(state));
        }
    }
}
